﻿
Imports System.CodeDom.Compiler
Imports System.IO

Public MustInherit Class configuration_project
    Private Class configuration
        Public ReadOnly files As IList(Of String) = Nothing
        Public ReadOnly output As String = Nothing
        Public ReadOnly opinions As IDictionary(Of String, String) = Nothing
        Public ReadOnly referenced_assemblies As IList(Of String) = Nothing
        Public ReadOnly compiler_opinions As String = Nothing
        Public ReadOnly entry_point As String = Nothing
        Public ReadOnly embedded_resources As IList(Of String) = Nothing
        Public ReadOnly linked_resources As IList(Of String) = Nothing
        Public ReadOnly valid As Boolean = False

        Private Shared Function split(ByVal l As String,
                                      ByRef left As String,
                                      ByRef right As String,
                                      ByVal separator As Char) As Boolean
            If l Is Nothing Then
                Return False
            Else
                Dim rtn() As String = Nothing
                rtn = l.Split(separator)
                If rtn Is Nothing OrElse rtn.Length() <> 2 Then
                    Return False
                Else
                    For i As Int32 = 0 To rtn.Length() - 1
                        rtn(i) = rtn(i).Trim()
                    Next
                    left = rtn(0)
                    right = rtn(1)
                    Return True
                End If
            End If
        End Function

        Private Shared Function parse(ByVal l As String, ByRef key As String, ByRef value As String) As Boolean
            If String.IsNullOrWhiteSpace(l) Then
                Return False
            ElseIf split(l, key, value, "="c) Then
                Return Not String.IsNullOrWhiteSpace(key) AndAlso
                       key(0) <> "#"c
            Else
                Return False
            End If
        End Function

        Private Function check_valid() As Boolean
            If files.Count = 0 AndAlso String.IsNullOrWhiteSpace(compiler_opinions) Then
                Console.WriteLine("no file & compiler_opinions defined in configuration")
                Return False
            ElseIf String.IsNullOrWhiteSpace(output) Then
                Console.WriteLine("no valid output defined in configuration")
                Return False
            ElseIf String.IsNullOrWhiteSpace(entry_point) Then
                Console.WriteLine("no valid entry point defined in configuration")
                Return False
            Else
                Return True
            End If
        End Function

        Public Sub New(ByVal file As String)
            files = New List(Of String)()
            opinions = New Dictionary(Of String, String)()
            referenced_assemblies = New List(Of String)()
            embedded_resources = New List(Of String)()
            linked_resources = New List(Of String)()

            Using reader As TextReader = New StreamReader(file)
                Dim l As String = Nothing
                l = reader.ReadLine()
                While Not l Is Nothing
                    Dim key As String = Nothing
                    Dim value As String = Nothing
                    If parse(l, key, value) Then
                        Select Case key.ToLower()
                            Case "file"
                                files.Add(value)
                            Case "output"
                                output = value
                            Case "opinion"
                                Dim k As String = Nothing
                                Dim v As String = Nothing
                                If split(value, k, v, ","c) Then
                                    opinions.Add(k, v)
                                End If
                            Case "referenced_assembly"
                                referenced_assemblies.Add(value)
                            Case "compiler_options"
                                compiler_opinions = value
                            Case "entry_point"
                                entry_point = value
                            Case "embedded_resource"
                                embedded_resources.Add(value)
                            Case "linked_resource"
                                linked_resources.Add(value)
                        End Select
                    End If
                    l = reader.ReadLine()
                End While
            End Using

            valid = check_valid()
        End Sub
    End Class

    Protected MustOverride Function codeprovider(ByVal opinions As IDictionary(Of String, String)) As CodeDomProvider

    Public Sub main(ByVal args() As String)
#If DEBUG Then
        Console.Write(String.Concat("press any key to continue...", vbCr))
        Console.ReadKey()
#End If
        If args Is Nothing OrElse args.Length() = 0 Then
            Console.WriteLine("need an input configuration file")
        Else
            Dim config As configuration = Nothing
            config = New configuration(args(0))
            If config.valid Then
                Dim cp As CodeDomProvider = Nothing
                cp = codeprovider(config.opinions)
                Dim p As CompilerParameters = Nothing
                p = New CompilerParameters()
                p.GenerateExecutable() = True
                p.GenerateInMemory() = False
                p.CompilerOptions() = config.compiler_opinions
                p.OutputAssembly() = config.output
                For Each v In config.referenced_assemblies
                    p.ReferencedAssemblies().Add(v)
                Next
                If cp.Supports(GeneratorSupport.EntryPointMethod) Then
                    p.MainClass() = config.entry_point
                End If
                If cp.Supports(GeneratorSupport.Resources) Then
                    For Each s As String In config.embedded_resources
                        p.EmbeddedResources().Add(s)
                    Next
                    For Each s As String In config.linked_resources
                        p.LinkedResources().Add(s)
                    Next
                End If

                Dim r As CompilerResults = Nothing
                r = cp.CompileAssemblyFromFile(p, config.files.ToArray())
                If r.Errors().HasErrors() Then
                    For Each e As CompilerError In r.Errors
                        Console.WriteLine(e)
                    Next
                End If
            Else
                Console.WriteLine(String.Concat("failed to parse configuration file ", args(0)))
            End If
        End If
    End Sub
End Class
